package com.ruoyi.course.UtilMCTSCourseScheduling;

import com.ruoyi.classroom.domain.classroom;
import com.ruoyi.classroom.service.IclassroomService;
import com.ruoyi.course.domain.ResourceAvailability;
import com.ruoyi.course.domain.vo.TimetableVO;
import com.ruoyi.course.mapper.ResourceAvailabilityMapper;
import com.ruoyi.course.service.IResourceAvailabilityService;
import com.ruoyi.trainingManagement.domain.CourseScheduleDetails;
import com.ruoyi.trainingManagement.mapper.CourseScheduleDetailsMapper;
import com.ruoyi.trainingManagement.mapper.CourseStudentInfoMapper;
import com.ruoyi.trainingManagement.service.ICourseStudentInfoService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.DayOfWeek;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 数据预处理器 - 整合了数据加载、校验、冲突检测和约束整理的完整逻辑
 */
@Component
public class DataPreprocessor {
    // 持有最新的预处理结果（避免重复处理）
    private SchedulingData latestSchedulingData;

    // 新增：课程班ID到学生ID列表的映射（解决空指针核心）
    private Map<String, List<String>> studentIdsByCourseClassId = new HashMap<>();

    // 获取最新预处理结果的getter（解决外部组件获取数据的需求）
    public SchedulingData getLatestSchedulingData() {
        return latestSchedulingData;
    }

    // 新增：提供学生ID与课程班的映射关系给DataAccessor
    public Map<String, List<String>> getStudentIdsByCourseClassId() {
        return Collections.unmodifiableMap(studentIdsByCourseClassId);
    }

    // 日志工具
    private static final Logger logger = LoggerFactory.getLogger(DataPreprocessor.class);

    // 课时划分常量：一天10节课，分3个时段（禁止跨时段排课）
    public static final Map<Integer, String> SESSION_TO_PERIOD = new HashMap<>(); // 节次→时段（上午/下午/晚上）
    public static final Map<Integer, String> SESSION_DESCRIPTION = new HashMap<>(); // 节次→描述（如“上午第1节”）

    // 业务规则常量
    private static final int MIN_CLASSROOM_CAPACITY = 10; // 最小教室容量
    private static final int MAX_WEEK_NUMBER = 20; // 最大周数
    private static final int DEFAULT_LESSON_COUNT = 2; // 默认每周上课次数
    private static final int DEFAULT_WEEK_TYPE = 2; // 默认每次课课时数

    @Autowired
    private CourseStudentInfoMapper courseStudentInfoMapper;

    static {
        // 初始化：节次→时段映射（1-4=上午，5-8=下午，9-10=晚上）
        SESSION_TO_PERIOD.put(1, "上午");
        SESSION_TO_PERIOD.put(2, "上午");
        SESSION_TO_PERIOD.put(3, "上午");
        SESSION_TO_PERIOD.put(4, "上午");
        SESSION_TO_PERIOD.put(5, "下午");
        SESSION_TO_PERIOD.put(6, "下午");
        SESSION_TO_PERIOD.put(7, "下午");
        SESSION_TO_PERIOD.put(8, "下午");
        SESSION_TO_PERIOD.put(9, "晚上");
        SESSION_TO_PERIOD.put(10, "晚上");

        // 初始化：节次→描述（用于展示）
        SESSION_DESCRIPTION.put(1, "上午第1节");
        SESSION_DESCRIPTION.put(2, "上午第2节");
        SESSION_DESCRIPTION.put(3, "上午第3节");
        SESSION_DESCRIPTION.put(4, "上午第4节");
        SESSION_DESCRIPTION.put(5, "下午第1节");
        SESSION_DESCRIPTION.put(6, "下午第2节");
        SESSION_DESCRIPTION.put(7, "下午第3节");
        SESSION_DESCRIPTION.put(8, "下午第4节");
        SESSION_DESCRIPTION.put(9, "晚上第1节");
        SESSION_DESCRIPTION.put(10, "晚上第2节");
    }

    @Autowired
    private CourseScheduleDetailsMapper courseScheduleDetailsMapper;

    @Autowired
    private ResourceAvailabilityMapper resourceAvailabilityMapper;

    @Autowired
    private IResourceAvailabilityService resourceAvailabilityService;

    @Autowired
    private ICourseStudentInfoService courseStudentInfoService;

    @Autowired
    private IclassroomService classroomService;

    @Autowired
    private DataAccessor dataAccessor; // 注入DataAccessor用于同步数据

    /**
     * 步骤3.3具体实现：加载原始资源数据
     * 作用：从基础数据中提取不可修改的原始资源（教师、教室、学生时间槽等）
     */
    public void loadOriginalResourceData(SchedulingData schedulingData) {
        if (schedulingData == null) {
            logger.error("schedulingData为null，无法加载原始资源数据");
            throw new IllegalArgumentException("schedulingData不能为null");
        }

        // 1. 加载学生时间槽数据（从schedulingData中获取预处理后的学生占用情况）
        this.latestSchedulingData.getStudentOccupancy().putAll(schedulingData.getStudentOccupancy());
        // 验证学生时间槽数据完整性
        if (this.latestSchedulingData.getStudentOccupancy().isEmpty()) {
            logger.warn("学生时间槽数据为空，可能影响排课结果准确性");
        } else {
            logger.debug("成功加载学生时间槽数据: {} 条记录",
                    this.latestSchedulingData.getStudentOccupancy().size());
        }

        // 2. 加载教师原始占用数据（从schedulingData中获取教师已占用的时间槽）
        this.latestSchedulingData.getTeacherOccupancy().putAll(schedulingData.getTeacherOccupancy());
        if (this.latestSchedulingData.getTeacherOccupancy().isEmpty()) {
            logger.warn("教师占用数据为空，可能是DataPreprocessor未正确加载数据");
        } else {
            logger.debug("成功加载教师占用数据: {} 位教师",
                    this.latestSchedulingData.getTeacherOccupancy().size());
        }

        // 3. 加载教室原始占用数据（从schedulingData中获取教室已占用的时间槽）
        this.latestSchedulingData.getClassroomOccupancy().putAll(schedulingData.getClassroomOccupancy());
        if (this.latestSchedulingData.getClassroomOccupancy().isEmpty()) {
            logger.warn("教室占用数据为空，可能是DataPreprocessor未正确加载数据");
        } else {
            logger.debug("成功加载教室占用数据: {} 间教室",
                    this.latestSchedulingData.getClassroomOccupancy().size());
        }

        // 4. 加载所有可用时间槽（从schedulingData中获取系统预定义的可用时间段）
        this.latestSchedulingData.getAllAvailableTimeSlots().addAll(schedulingData.getAllAvailableTimeSlots());
        if (this.latestSchedulingData.getAllAvailableTimeSlots().isEmpty()) {
            logger.error("可用时间槽数据为空，将导致无法排课");
            throw new IllegalStateException("可用时间槽数据初始化失败");
        } else {
            logger.debug("成功加载可用时间槽数据: {} 个时间段",
                    this.latestSchedulingData.getAllAvailableTimeSlots().size());
        }

        // 5. 日志输出加载结果（便于调试和数据校验）
        logger.info("原始资源数据加载完成：");
        logger.info(" - 学生时间槽覆盖学生数: {}", latestSchedulingData.getStudentOccupancy().size());
        logger.info(" - 教师占用数据覆盖教师数: {}", latestSchedulingData.getTeacherOccupancy().size());
        logger.info(" - 教室占用数据覆盖教室数: {}", latestSchedulingData.getClassroomOccupancy().size());
        logger.info(" - 可用时间槽总数: {}", latestSchedulingData.getAllAvailableTimeSlots().size());
    }

    /**
     * 内部类：课程班信息
     */
    public static class CourseClassInfo {
        private String id;
        private String courseId;
        private String preferredTeacherId;
        private String studentGroupId;
        private int sessionsPerMeeting;
        private int meetingsPerWeek;
        private int startWeek;
        private int endWeek;
        private int studentCount;
        private int priority;
        private int semester;
        private int academicYear;
        private String className;
        private String department;
        private int campus; // 校区（int类型，与数据库一致）
        private int roomType; // 教室类型要求（int类型）
        private int lessonCount; // 每周上课次数（如2次/周）
        private int weekType;    // 每次课课时数（如2课时/次）

        /**
         * 构造函数
         */
        public CourseClassInfo(String id, String courseId, String preferredTeacherId,
                               String studentGroupId, int sessionsPerMeeting, int meetingsPerWeek,
                               int startWeek, int endWeek, int studentCount, int priority,
                               int semester, int academicYear, String className,
                               String department, int campus, int lessonCount, int weekType) {
            this.id = id;
            this.courseId = courseId;
            this.preferredTeacherId = preferredTeacherId;
            this.studentGroupId = studentGroupId;
            this.sessionsPerMeeting = sessionsPerMeeting;
            this.meetingsPerWeek = meetingsPerWeek;
            this.startWeek = startWeek;
            this.endWeek = endWeek;
            this.studentCount = studentCount;
            this.priority = priority;
            this.semester = semester;
            this.academicYear = academicYear;
            this.className = className;
            this.department = department;
            this.campus = campus;
            this.lessonCount = lessonCount;
            this.weekType = weekType;
        }

        /**
         * 校验课程基础信息是否完整
         */
        public boolean isBasicInfoComplete() {
            boolean valid = lessonCount > 0
                    && weekType > 0
                    && startWeek > 0
                    && endWeek >= startWeek;

            if (!valid) {
                logger.warn("课程{}基础信息不完整：每周次数={}, 每次课时={}, 周范围={}-{}",
                        id, lessonCount, weekType, startWeek, endWeek);
            }
            return valid;
        }

        // 计算总需要安排的课时数
        public int getTotalSessionsNeeded() {
            int weeks = endWeek - startWeek + 1;
            return meetingsPerWeek * weeks;
        }

        // 计算课程每周需要的时间槽数量（每周次数 × 每次课时）
        public int getWeeklyRequiredSlots() {
            return lessonCount * weekType;
        }

        // 计算课程总周数
        public int getTotalWeeks() {
            return endWeek - startWeek + 1;
        }

        public int getContinuousSlotsPerSession() {
            return Math.max(1, this.weekType);
        }

        // Getters and Setters
        public String getId() { return id; }
        public void setId(String id) { this.id = id; }

        public String getCourseId() { return courseId; }
        public void setCourseId(String courseId) { this.courseId = courseId; }

        public String getPreferredTeacherId() { return preferredTeacherId; }
        public void setPreferredTeacherId(String preferredTeacherId) { this.preferredTeacherId = preferredTeacherId; }

        public String getStudentGroupId() { return studentGroupId; }
        public void setStudentGroupId(String studentGroupId) { this.studentGroupId = studentGroupId; }

        public int getSessionsPerMeeting() { return sessionsPerMeeting; }
        public void setSessionsPerMeeting(int sessionsPerMeeting) { this.sessionsPerMeeting = sessionsPerMeeting; }

        public int getMeetingsPerWeek() { return meetingsPerWeek; }
        public void setMeetingsPerWeek(int meetingsPerWeek) { this.meetingsPerWeek = meetingsPerWeek; }

        public int getStartWeek() { return startWeek; }
        public void setStartWeek(int startWeek) { this.startWeek = startWeek; }

        public int getEndWeek() { return endWeek; }
        public void setEndWeek(int endWeek) { this.endWeek = endWeek; }

        public int getStudentCount() { return studentCount; }
        public void setStudentCount(int studentCount) { this.studentCount = studentCount; }

        public int getPriority() { return priority; }
        public void setPriority(int priority) { this.priority = priority; }

        public int getSemester() { return semester; }
        public void setSemester(int semester) { this.semester = semester; }

        public int getAcademicYear() { return academicYear; }
        public void setAcademicYear(int academicYear) { this.academicYear = academicYear; }

        public String getClassName() { return className; }
        public void setClassName(String className) { this.className = className; }

        public String getDepartment() { return department; }
        public void setDepartment(String department) { this.department = department; }

        public int getCampus() { return campus; }
        public void setCampus(int campus) { this.campus = campus; }

        public long getRoomType() { return roomType; }
        public void setRoomType(int roomType) { this.roomType = roomType; }

        public int getLessonCount() { return lessonCount; }
        public void setLessonCount(int lessonCount) { this.lessonCount = lessonCount; }

        public int getWeekType() { return weekType; }
        public void setWeekType(int weekType) { this.weekType = weekType; }

        @Override
        public String toString() {
            return "CourseClassInfo{" +
                    "id='" + id + '\'' +
                    ", className='" + className + '\'' +
                    ", lessonCount=" + lessonCount +
                    ", weekType=" + weekType +
                    ", startWeek=" + startWeek +
                    ", endWeek=" + endWeek +
                    ", studentCount=" + studentCount +
                    ", priority=" + priority +
                    '}';
        }
    }

    /**
     * 内部类：教师信息
     */
    public static class TeacherInfo {
        private String id;
        private Set<String> teachableCourses; // 能教授的课程ID集合
        private Set<String> assignedCourseClasses; // 分配的课程班ID集合

        public TeacherInfo(String id) {
            this.id = id;
            this.teachableCourses = new HashSet<>();
            this.assignedCourseClasses = new HashSet<>();
        }

        public void addTeachableCourse(String courseId) {
            teachableCourses.add(courseId);
        }

        public void addAssignedCourseClass(String courseClassId) {
            assignedCourseClasses.add(courseClassId);
        }

        // 检查教师是否能教授某课程
        public boolean canTeach(String courseId) {
            return teachableCourses.contains(courseId);
        }

        // 获取教师的工作量（分配的课程班数量）
        public int getWorkload() {
            return assignedCourseClasses.size();
        }

        // Getters
        public String getId() { return id; }
        public Set<String> getTeachableCourses() { return teachableCourses; }
        public Set<String> getAssignedCourseClasses() { return assignedCourseClasses; }
    }

    /**
     * 内部类：预处理后的排课数据
     */
    public static class SchedulingData {
        private Map<String, CourseClassInfo> courseClasses;
        private Map<String, TeacherInfo> teachers;
        private Map<String, Set<TimeSlot>> teacherOccupancy;
        private Map<String, Set<TimeSlot>> classroomOccupancy;
        // 学生个体占用情况映射（key:学生ID，value:占用的时间槽集合）
        private Map<String, Set<TimeSlot>> studentOccupancy;
        private Set<TimeSlot> allAvailableTimeSlots;
        private List<String> validationErrors; // 数据校验错误信息
        private List<String> conflictWarnings; // 资源冲突警告

        public SchedulingData() {
            this.courseClasses = new HashMap<>();
            this.teachers = new HashMap<>();
            this.teacherOccupancy = new HashMap<>();
            this.classroomOccupancy = new HashMap<>();
            // 初始化学生占用情况Map
            this.studentOccupancy = new HashMap<>();
            this.allAvailableTimeSlots = new HashSet<>();
            this.validationErrors = new ArrayList<>();
            this.conflictWarnings = new ArrayList<>();
        }

        // Getters and Setters
        public Map<String, CourseClassInfo> getCourseClasses() { return courseClasses; }
        public void setCourseClasses(Map<String, CourseClassInfo> courseClasses) { this.courseClasses = courseClasses; }

        public Map<String, TeacherInfo> getTeachers() { return teachers; }
        public void setTeachers(Map<String, TeacherInfo> teachers) { this.teachers = teachers; }

        public Map<String, Set<TimeSlot>> getTeacherOccupancy() { return teacherOccupancy; }
        public void setTeacherOccupancy(Map<String, Set<TimeSlot>> teacherOccupancy) { this.teacherOccupancy = teacherOccupancy; }

        public Map<String, Set<TimeSlot>> getClassroomOccupancy() { return classroomOccupancy; }
        public void setClassroomOccupancy(Map<String, Set<TimeSlot>> classroomOccupancy) { this.classroomOccupancy = classroomOccupancy; }

        // 学生占用情况的getter和setter
        public Map<String, Set<TimeSlot>> getStudentOccupancy() { return studentOccupancy; }
        public void setStudentOccupancy(Map<String, Set<TimeSlot>> studentOccupancy) { this.studentOccupancy = studentOccupancy; }

        public Set<TimeSlot> getAllAvailableTimeSlots() { return allAvailableTimeSlots; }
        public void setAllAvailableTimeSlots(Set<TimeSlot> allAvailableTimeSlots) { this.allAvailableTimeSlots = allAvailableTimeSlots; }

        public List<String> getValidationErrors() { return validationErrors; }
        public List<String> getConflictWarnings() { return conflictWarnings; }

        public void addValidationError(String error) {
            logger.error("数据校验错误: {}", error);
            this.validationErrors.add(error);
        }

        public void addConflictWarning(String warning) {
            logger.warn("资源冲突警告: {}", warning);
            this.conflictWarnings.add(warning);
        }

        // 检查是否有严重错误
        public boolean hasCriticalErrors() {
            return !validationErrors.isEmpty();
        }
    }

    /**
     * 内部类：时间段表示（优化为节次展示，支持跨时段校验）
     */
    public static class TimeSlot {
        private DayOfWeek dayOfWeek;    // 星期（如周一）
        private int startSession;       // 开始节次（1-10）
        private int endSession;         // 结束节次（1-10）
        private LocalTime startTime;    // 节次对应的开始时间（如8:00）
        private LocalTime endTime;      // 节次对应的结束时间（如8:45）
        private String courseClassId;

        // 节次到时间段的映射（10节课制）
        public static final Map<Integer, LocalTime> SESSION_START_TIMES = new HashMap<>();
        public static final Map<Integer, LocalTime> SESSION_END_TIMES = new HashMap<>();
        private static final Map<Integer, String> SESSION_PERIOD = new HashMap<>();  // 上午/下午/晚上

        static {
            // 初始化10节课的时间映射
            // 上午4节课
            SESSION_START_TIMES.put(1, LocalTime.of(8, 0));
            SESSION_END_TIMES.put(1, LocalTime.of(8, 45));
            SESSION_PERIOD.put(1, "上午");

            SESSION_START_TIMES.put(2, LocalTime.of(8, 55));
            SESSION_END_TIMES.put(2, LocalTime.of(9, 40));
            SESSION_PERIOD.put(2, "上午");

            SESSION_START_TIMES.put(3, LocalTime.of(10, 0));
            SESSION_END_TIMES.put(3, LocalTime.of(10, 45));
            SESSION_PERIOD.put(3, "上午");

            SESSION_START_TIMES.put(4, LocalTime.of(10, 55));
            SESSION_END_TIMES.put(4, LocalTime.of(11, 40));
            SESSION_PERIOD.put(4, "上午");

            // 下午4节课
            SESSION_START_TIMES.put(5, LocalTime.of(13, 30));
            SESSION_END_TIMES.put(5, LocalTime.of(14, 15));
            SESSION_PERIOD.put(5, "下午");

            SESSION_START_TIMES.put(6, LocalTime.of(14, 25));
            SESSION_END_TIMES.put(6, LocalTime.of(15, 10));
            SESSION_PERIOD.put(6, "下午");

            SESSION_START_TIMES.put(7, LocalTime.of(15, 30));
            SESSION_END_TIMES.put(7, LocalTime.of(16, 15));
            SESSION_PERIOD.put(7, "下午");

            SESSION_START_TIMES.put(8, LocalTime.of(16, 25));
            SESSION_END_TIMES.put(8, LocalTime.of(17, 10));
            SESSION_PERIOD.put(8, "下午");

            // 晚上2节课
            SESSION_START_TIMES.put(9, LocalTime.of(18, 30));
            SESSION_END_TIMES.put(9, LocalTime.of(19, 15));
            SESSION_PERIOD.put(9, "晚上");

            SESSION_START_TIMES.put(10, LocalTime.of(19, 25));
            SESSION_END_TIMES.put(10, LocalTime.of(20, 10));
            SESSION_PERIOD.put(10, "晚上");
        }

        @Override
        public String toString() {
            String chineseDay = getChineseDay(dayOfWeek);
            String sessionPart;

            if (startSession == endSession) {
                sessionPart = "第" + startSession + "节";
            } else {
                sessionPart = "第" + startSession + "-" + endSession + "节";
            }

            // 格式化时间显示（HH:mm）
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm");
            String timePart = String.format("(%s-%s)",
                    startTime.format(formatter),
                    endTime.format(formatter));

            return chineseDay + sessionPart + " " + timePart;
        }

        public String toFullString(int startWeek, int endWeek) {
            return toString() + " 第" + startWeek + "-" + endWeek + "周";
        }

        /**
         * 基于节次创建时间槽
         */
        public TimeSlot(DayOfWeek dayOfWeek, int startSession, int endSession) {
            this.dayOfWeek = dayOfWeek;
            this.startSession = startSession;
            this.endSession = endSession;
            this.startTime = SESSION_START_TIMES.get(startSession);
            this.endTime = SESSION_END_TIMES.get(endSession);
        }

        /**
         * 核心冲突校验：判断两个时间段模板是否相同（星期+节次范围重叠）
         * 注：周范围冲突需在外部结合课程的startWeek/endWeek判断
         */
        public boolean isSamePeriod(TimeSlot other) {
            if (this.dayOfWeek != other.dayOfWeek) return false;
            // 节次范围重叠判断（如1-2节 和 2-3节 视为重叠）
            return !(this.endSession < other.startSession || this.startSession > other.endSession);
        }

        /**
         * 格式化显示：明确“连续占用”的周范围
         * 例如：“周一第1节（第1-8周）”
         */
        public String toPeriodString(int startWeek, int endWeek) {
            String chineseDay = getChineseDay(dayOfWeek);
            if (startSession == endSession) {
                return chineseDay + "第" + startSession + "节（第" + startWeek + "-" + endWeek + "周）";
            } else {
                return chineseDay + "第" + startSession + "-" + endSession + "节（第" + startWeek + "-" + endWeek + "周）";
            }
        }

        /**
         * 从数据库记录创建TimeSlot，支持节次转换
         */
        public static TimeSlot fromDatabase(int dayOfWeek, String startTimeStr, String endTimeStr) {
            DayOfWeek day = DayOfWeek.of(dayOfWeek);

            // 解析开始节次
            int startSession = parseSession(startTimeStr);
            // 解析结束节次
            int endSession = parseSession(endTimeStr);

            // 如果解析失败，使用默认节次
            if (startSession <= 0) {
                startSession = 1;
                logger.warn("开始节次解析失败，使用默认节次: 1");
            }
            if (endSession <= 0 || endSession < startSession) {
                endSession = startSession;
                logger.warn("结束节次解析失败，使用默认节次: {}", startSession);
            }

            return new TimeSlot(day, startSession, endSession);
        }

        /**
         * 从ResourceAvailability创建TimeSlot
         */
        public static TimeSlot fromResourceAvailability(ResourceAvailability resource) {
            int dayOfWeek = resource.getDayOfWeek();
            String startTimeStr = resource.getStartTime();
            String endTimeStr = resource.getEndTime();

            return fromDatabase(dayOfWeek, startTimeStr, endTimeStr);
        }

        /**
         * 解析时间字符串为节次
         */
        private static int parseSession(String timeStr) {
            if (timeStr == null || timeStr.trim().isEmpty()) {
                logger.warn("时间字符串为空，无法解析为节次");
                return -1;
            }

            // 尝试直接解析为节次数字
            try {
                return Integer.parseInt(timeStr);
            } catch (NumberFormatException e) {
                // 处理"第X节"格式
                if (timeStr.startsWith("第") && timeStr.contains("节")) {
                    String numStr = timeStr.substring(1, timeStr.indexOf("节"));
                    return Integer.parseInt(numStr);
                }

                // 尝试通过时间匹配节次
                try {
                    LocalTime time = LocalTime.parse(timeStr);
                    for (Map.Entry<Integer, LocalTime> entry : SESSION_START_TIMES.entrySet()) {
                        // 时间在当前节次范围内
                        if (!time.isBefore(entry.getValue()) &&
                                !time.isAfter(SESSION_END_TIMES.get(entry.getKey()))) {
                            return entry.getKey();
                        }
                    }
                } catch (DateTimeParseException ex) {
                    logger.error("无法解析时间字符串为节次: {}", timeStr, ex);
                    return -1;
                }

                logger.error("无法解析时间字符串为节次: {}", timeStr);
                return -1;
            }
        }

        /**
         * 检查时间槽是否有效
         */
        public boolean isValid() {
            // 检查节次范围有效性
            if (startSession < 1 || startSession > 10) {
                logger.warn("无效的开始节次: {}", startSession);
                return false;
            }

            if (endSession < 1 || endSession > 10) {
                logger.warn("无效的结束节次: {}", endSession);
                return false;
            }

            // 检查开始节次是否小于等于结束节次
            if (startSession > endSession) {
                logger.warn("开始节次({})大于结束节次({})", startSession, endSession);
                return false;
            }

            // 检查时间有效性
            if (startTime == null || endTime == null) {
                logger.warn("时间映射为空，节次: {}-{}", startSession, endSession);
                return false;
            }

            // 检查时间顺序
            if (!startTime.isBefore(endTime)) {
                logger.warn("开始时间({})不早于结束时间({})", startTime, endTime);
                return false;
            }

            // 检查是否跨时段（如果是多节课程）
            if (startSession != endSession && isCrossPeriod()) {
                logger.warn("时间槽跨时段: {}-{}节", startSession, endSession);
                return false;
            }
            return true;
        }

        /**
         * 检查是否跨时段（上午/下午/晚上）
         */
        public boolean isCrossPeriod() {
            if (startSession == endSession) {
                return false; // 单节课程不会跨时段
            }
            // 检查开始和结束节次是否属于同一时段
            return !SESSION_PERIOD.get(startSession).equals(SESSION_PERIOD.get(endSession));
        }

        /**
         * 检查两个时间段是否重叠
         */
        public boolean overlaps(TimeSlot other) {
            if (this.dayOfWeek != other.dayOfWeek) {
                return false;
            }

            // 节次重叠判断
            return !(this.endSession < other.startSession || this.startSession > other.endSession);
        }

        /**
         * 将DayOfWeek转换为中文星期
         */
        private String getChineseDay(DayOfWeek day) {
            switch (day) {
                case MONDAY: return "周一";
                case TUESDAY: return "周二";
                case WEDNESDAY: return "周三";
                case THURSDAY: return "周四";
                case FRIDAY: return "周五";
                case SATURDAY: return "周六";
                case SUNDAY: return "周日";
                default: return "未知";
            }
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            TimeSlot timeSlot = (TimeSlot) o;
            return startSession == timeSlot.startSession &&
                    endSession == timeSlot.endSession &&
                    dayOfWeek == timeSlot.dayOfWeek;
        }

        @Override
        public int hashCode() {
            return Objects.hash(dayOfWeek, startSession, endSession);
        }


        // Getters
        public DayOfWeek getDayOfWeek() { return dayOfWeek; }
        public int getStartSession() { return startSession; }
        public int getEndSession() { return endSession; }
        public LocalTime getStartTime() { return startTime; }
        public LocalTime getEndTime() { return endTime; }
        public String getPeriod() { return SESSION_PERIOD.get(startSession); }

        public String getCourseClassId() {
            return courseClassId;
        }

        public void setCourseClassId(String courseClassId) {
            this.courseClassId = courseClassId;
        }
    }

    public static List<String> getExistingCourseScheduleIds() {
        // 实现逻辑：从数据源（如数据库）查询所有已存在的课程安排ID
        return new ArrayList<>(); // 实际项目中替换为真实查询
    }

    public static List<TimeSlot> getTimeSlotsByScheduleId(String scheduleId) {
        // 实现逻辑：根据安排ID查询时间槽
        return new ArrayList<>(); // 实际项目中替换为真实查询
    }


    /**
     * 内部类：排课约束条件
     */
    public static class SchedulingConstraints {
        // 硬性约束
        private boolean enforceClassroomCapacity; // 教室容量必须大于等于学生数
        private boolean enforceNoOverlaps; // 不允许时间重叠
        private boolean enforceTimeRange; // 必须在指定时间范围内排课

        // 软性约束权重（值越大表示越重要）
        private int teacherLoadBalanceWeight; // 教师负载均衡
        private int courseDistributionWeight; // 课程时间分布
        private int sameCourseIntervalWeight; // 同一课程间隔

        // 时间间隔约束（天）
        private int minDaysBetweenSameCourse; // 同一课程两次课的最小间隔
        private int maxDaysBetweenSameCourse; // 同一课程两次课的最大间隔

        // 每天最大课时
        private int maxSessionsPerDay;

        public SchedulingConstraints() {
            // 默认约束配置（适配用户需求：3-4天间隔）
            this.enforceClassroomCapacity = true;
            this.enforceNoOverlaps = true;
            this.enforceTimeRange = true;

            this.teacherLoadBalanceWeight = 3;
            this.courseDistributionWeight = 2;
            this.sameCourseIntervalWeight = 3;

            this.minDaysBetweenSameCourse = 3; // 最小间隔3天
            this.maxDaysBetweenSameCourse = 4; // 最大间隔4天
            this.maxSessionsPerDay = 3;
        }

        // Getters and Setters
        public boolean isEnforceClassroomCapacity() { return enforceClassroomCapacity; }
        public void setEnforceClassroomCapacity(boolean enforceClassroomCapacity) { this.enforceClassroomCapacity = enforceClassroomCapacity; }

        public boolean isEnforceNoOverlaps() { return enforceNoOverlaps; }
        public void setEnforceNoOverlaps(boolean enforceNoOverlaps) { this.enforceNoOverlaps = enforceNoOverlaps; }

        public boolean isEnforceTimeRange() { return enforceTimeRange; }
        public void setEnforceTimeRange(boolean enforceTimeRange) { this.enforceTimeRange = enforceTimeRange; }

        public int getTeacherLoadBalanceWeight() { return teacherLoadBalanceWeight; }
        public void setTeacherLoadBalanceWeight(int teacherLoadBalanceWeight) { this.teacherLoadBalanceWeight = teacherLoadBalanceWeight; }

        public int getCourseDistributionWeight() { return courseDistributionWeight; }
        public void setCourseDistributionWeight(int courseDistributionWeight) { this.courseDistributionWeight = courseDistributionWeight; }

        public int getSameCourseIntervalWeight() { return sameCourseIntervalWeight; }
        public void setSameCourseIntervalWeight(int sameCourseIntervalWeight) { this.sameCourseIntervalWeight = sameCourseIntervalWeight; }

        public int getMinDaysBetweenSameCourse() { return minDaysBetweenSameCourse; }
        public void setMinDaysBetweenSameCourse(int minDaysBetweenSameCourse) { this.minDaysBetweenSameCourse = minDaysBetweenSameCourse; }

        public int getMaxDaysBetweenSameCourse() { return maxDaysBetweenSameCourse; }
        public void setMaxDaysBetweenSameCourse(int maxDaysBetweenSameCourse) { this.maxDaysBetweenSameCourse = maxDaysBetweenSameCourse; }

        public int getMaxSessionsPerDay() { return maxSessionsPerDay; }
        public void setMaxSessionsPerDay(int maxSessionsPerDay) { this.maxSessionsPerDay = maxSessionsPerDay; }
    }

    /**
     * 加载并处理排课所需的所有数据，包含完整的校验和冲突检测
     */
    public SchedulingData processSchedulingData(int semesterId, int academicYear) {
        SchedulingData data = new SchedulingData();
        logger.info("开始处理排课数据 - 学年: {}, 学期ID: {}", academicYear, semesterId);
        try {
            // 清空之前的学生ID映射
            studentIdsByCourseClassId.clear();

            loadBasicData(data, semesterId, academicYear);
            validateDataIntegrity(data, semesterId, academicYear);

            if (data.hasCriticalErrors()) {
                logger.error("数据校验发现 {} 个严重错误，无法继续处理", data.getValidationErrors().size());
                this.latestSchedulingData = data; // 即使有错误也持有，便于外部查看
                return data;
            }

            detectResourceConflicts(data);
            loadAvailableTimeSlots(data, semesterId, academicYear);
            printProcessingSummary(data);

            // 打印时间槽Map信息
            printTimeSlotMaps(data);

            // 数据预处理完成后打印所有剩余数据
            printFullSchedulingData(data);

            this.latestSchedulingData = data; // 处理成功后更新最新数据

            // 新增：同步学生ID映射到DataAccessor
            syncStudentIdsToAccessor();

        } catch (Exception e) {
            logger.error("处理排课数据时发生错误", e);
            data.addValidationError("处理排课数据时发生异常: " + e.getMessage());
            this.latestSchedulingData = data;
        }
        return data;
    }

    /**
     * 新增：将学生ID与课程班的映射关系同步到DataAccessor
     */
    private void syncStudentIdsToAccessor() {
        try {
            if (dataAccessor == null) {
                logger.error("DataAccessor未注入，无法同步学生ID映射");
                return;
            }

            // 确保传入的集合不为null
            Map<String, List<String>> safeMap = studentIdsByCourseClassId != null
                    ? studentIdsByCourseClassId
                    : new HashMap<>();

            dataAccessor.setStudentIdsByCourseClassId(safeMap);
            logger.info("成功同步学生ID与课程班映射关系，共 {} 个课程班", safeMap.size());
        } catch (Exception e) {
            logger.error("同步学生ID映射到DataAccessor失败", e);
        }
    }

    /**
     * 打印所有时间槽Map信息
     */
    private void printTimeSlotMaps(SchedulingData data) {
        logger.info("\n===== 时间槽Map信息 =====");

        // 打印教师时间槽
        logger.info("\n----- 教师时间槽 ({}) -----", data.getTeacherOccupancy().size());
        for (Map.Entry<String, Set<TimeSlot>> entry : data.getTeacherOccupancy().entrySet()) {
            logger.info("教师ID: {}, 占用时间槽数量: {}", entry.getKey(), entry.getValue().size());
            if (!entry.getValue().isEmpty() && logger.isDebugEnabled()) {
                for (TimeSlot slot : entry.getValue()) {
                    logger.debug("  - {}", slot);
                }
            }
        }

        // 打印教室时间槽
        logger.info("\n----- 教室时间槽 ({}) -----", data.getClassroomOccupancy().size());
        for (Map.Entry<String, Set<TimeSlot>> entry : data.getClassroomOccupancy().entrySet()) {
            logger.info("教室ID: {}, 占用时间槽数量: {}", entry.getKey(), entry.getValue().size());
            if (!entry.getValue().isEmpty() && logger.isDebugEnabled()) {
                for (TimeSlot slot : entry.getValue()) {
                    logger.debug("  - {}", slot);
                }
            }
        }
    }

    /**
     * 加载所有基础数据
     */
    private void loadBasicData(SchedulingData data, int semesterId, int academicYear) {
        logger.info("开始加载基础数据...");

        // 1. 加载课程班信息
        Map<String, CourseClassInfo> courseClasses = loadCourseClasses(semesterId, academicYear);
        data.setCourseClasses(courseClasses);
        logger.info("加载课程班信息完成: {} 个课程班", courseClasses.size());

        // 2. 从课程班信息中提取教师信息
        Map<String, TeacherInfo> teachers = extractTeachersFromCourseClasses(courseClasses);
        data.setTeachers(teachers);
        logger.info("提取教师信息完成: {} 位教师", teachers.size());

        // 为每个教师初始化时间槽集合（即使没有占用记录）
        for (String teacherId : teachers.keySet()) {
            data.getTeacherOccupancy().putIfAbsent(teacherId, new HashSet<>());
        }

        // 为每个教室初始化时间槽集合（即使没有占用记录）
        // 注意：这里保留教室占用记录但删除了教室信息本身

        // 3. 加载资源占用情况
        loadResourceOccupancy(data, semesterId, String.valueOf(academicYear));
        logger.info("加载资源占用情况完成");
    }

    /**
     * 执行数据有效性校验
     */
    private void validateDataIntegrity(SchedulingData data, int semesterId, int academicYear) {
        logger.info("开始执行数据有效性校验...");

        // 1. 校验课程班数据
        validateCourseClasses(data);

        // 2. 校验教师数据
        validateTeachers(data);

        // 3. 校验时间参数
        validateTimeParameters(data);

        logger.info("数据有效性校验完成 - 错误: {} 个, 警告: {} 个",
                data.getValidationErrors().size(), data.getConflictWarnings().size());
    }

    /**
     * 校验课程班数据完整性
     */
    private void validateCourseClasses(SchedulingData data) {
        Set<String> courseClassIds = new HashSet<>();

        for (CourseClassInfo courseClass : data.getCourseClasses().values()) {
            // 校验ID唯一性和有效性
            if (courseClass.getId() == null || courseClass.getId().trim().isEmpty()) {
                data.addValidationError("课程班信息缺失ID");
                continue;
            }

            if (courseClassIds.contains(courseClass.getId())) {
                data.addValidationError("课程班ID重复: " + courseClass.getId());
            } else {
                courseClassIds.add(courseClass.getId());
            }

            // 校验教师ID有效性
            if (courseClass.getPreferredTeacherId() == null ||
                    courseClass.getPreferredTeacherId().trim().isEmpty()) {
                data.addValidationError("课程班 " + courseClass.getId() + " 未分配教师");
            } else if (!data.getTeachers().containsKey(courseClass.getPreferredTeacherId())) {
                data.addValidationError("课程班 " + courseClass.getId() + " 引用了不存在的教师: " +
                        courseClass.getPreferredTeacherId());
            }

            // 校验学生数量合理性
            if (courseClass.getStudentCount() <= 0) {
                data.addValidationError("课程班 " + courseClass.getId() + " 学生数量无效: " +
                        courseClass.getStudentCount());
            }

            // 校验周次范围合理性
            if (courseClass.getStartWeek() <= 0 || courseClass.getEndWeek() <= 0 ||
                    courseClass.getStartWeek() > courseClass.getEndWeek()) {
                data.addValidationError("课程班 " + courseClass.getId() + " 周次范围无效: " +
                        courseClass.getStartWeek() + "-" + courseClass.getEndWeek());
            } else if (courseClass.getEndWeek() > MAX_WEEK_NUMBER) {
                data.addConflictWarning("课程班 " + courseClass.getId() + " 周次超出最大允许值: " +
                        courseClass.getEndWeek() + " (最大允许值: " + MAX_WEEK_NUMBER + ")");
            }

            // 校验lessonCount和weekType有效性
            if (courseClass.getLessonCount() <= 0) {
                data.addConflictWarning("课程班 " + courseClass.getId() + " 每周上课次数无效: " +
                        courseClass.getLessonCount() + "，已重置为默认值" + DEFAULT_LESSON_COUNT);
                courseClass.setLessonCount(DEFAULT_LESSON_COUNT);
            }
            if (courseClass.getWeekType() <= 0) {
                data.addConflictWarning("课程班 " + courseClass.getId() + " 每次课课时数无效: " +
                        courseClass.getWeekType() + "，已重置为默认值" + DEFAULT_WEEK_TYPE);
                courseClass.setWeekType(DEFAULT_WEEK_TYPE);
            }

            // 校验课时数合理性
            int totalSessions = courseClass.getTotalSessionsNeeded();
            if (totalSessions <= 0) {
                data.addValidationError("课程班 " + courseClass.getId() + " 总课时数无效: " + totalSessions);
            }

            // 校验学生ID映射是否存在
            if (!studentIdsByCourseClassId.containsKey(courseClass.getId())) {
                data.addConflictWarning("课程班 " + courseClass.getId() + " 未找到关联的学生ID列表");
            } else if (studentIdsByCourseClassId.get(courseClass.getId()).isEmpty()) {
                data.addConflictWarning("课程班 " + courseClass.getId() + " 关联的学生ID列表为空");
            }
        }
    }

    /**
     * 校验教师数据完整性
     */
    private void validateTeachers(SchedulingData data) {
        Set<String> teacherIds = new HashSet<>();

        for (TeacherInfo teacher : data.getTeachers().values()) {
            // 校验ID唯一性和有效性
            if (teacher.getId() == null || teacher.getId().trim().isEmpty()) {
                data.addValidationError("教师信息缺失ID");
                continue;
            }

            if (teacherIds.contains(teacher.getId())) {
                data.addValidationError("教师ID重复: " + teacher.getId());
            } else {
                teacherIds.add(teacher.getId());
            }

            // 校验可教课程不为空
            if (teacher.getTeachableCourses().isEmpty()) {
                data.addConflictWarning("教师 " + teacher.getId() + " 没有可教课程记录");
            }
        }
    }

    /**
     * 校验时间参数合理性
     */
    private void validateTimeParameters(SchedulingData data) {
        // 校验教师占用时间段
        for (Map.Entry<String, Set<TimeSlot>> entry : data.getTeacherOccupancy().entrySet()) {
            String teacherId = entry.getKey();
            for (TimeSlot slot : entry.getValue()) {
                if (!slot.isValid()) {
                    data.addValidationError("教师 " + teacherId + " 的时间段无效: " + slot);
                }
            }
        }

        // 校验教室占用时间段
        for (Map.Entry<String, Set<TimeSlot>> entry : data.getClassroomOccupancy().entrySet()) {
            String classroomId = entry.getKey();
            for (TimeSlot slot : entry.getValue()) {
                if (!slot.isValid()) {
                    data.addValidationError("教室 " + classroomId + " 的时间段无效: " + slot);
                }
            }
        }
    }

    /**
     * 检测资源冲突
     */
    private void detectResourceConflicts(SchedulingData data) {
        logger.info("开始执行资源冲突检测...");

        // 1. 检测教师时间冲突
        detectTeacherConflicts(data);

        // 2. 检测教室时间冲突
        detectClassroomConflicts(data);

        logger.info("资源冲突检测完成 - 警告: {} 个", data.getConflictWarnings().size());
    }

    /**
     * 检测教师时间冲突
     */
    private void detectTeacherConflicts(SchedulingData data) {
        for (Map.Entry<String, Set<TimeSlot>> entry : data.getTeacherOccupancy().entrySet()) {
            String teacherId = entry.getKey();
            Set<TimeSlot> slots = entry.getValue();
            List<TimeSlot> slotList = new ArrayList<>(slots);

            // 检查每对时间段是否有重叠
            for (int i = 0; i < slotList.size(); i++) {
                for (int j = i + 1; j < slotList.size(); j++) {
                    TimeSlot slot1 = slotList.get(i);
                    TimeSlot slot2 = slotList.get(j);

                    if (slot1.overlaps(slot2)) {
                        data.addConflictWarning("教师 " + teacherId + " 存在时间冲突: " +
                                slot1 + " 与 " + slot2);
                    }
                }
            }
        }
    }

    /**
     * 检测教室时间冲突
     */
    private void detectClassroomConflicts(SchedulingData data) {
        for (Map.Entry<String, Set<TimeSlot>> entry : data.getClassroomOccupancy().entrySet()) {
            String classroomId = entry.getKey();
            Set<TimeSlot> slots = entry.getValue();
            List<TimeSlot> slotList = new ArrayList<>(slots);

            // 检查每对时间段是否有重叠
            for (int i = 0; i < slotList.size(); i++) {
                for (int j = i + 1; j < slotList.size(); j++) {
                    TimeSlot slot1 = slotList.get(i);
                    TimeSlot slot2 = slotList.get(j);

                    if (slot1.overlaps(slot2)) {
                        data.addConflictWarning("教室 " + classroomId + " 存在时间冲突: " +
                                slot1 + " 与 " + slot2);
                    }
                }
            }
        }
    }

    /**
     * 获取排课约束条件
     */
    public SchedulingConstraints getSchedulingConstraints() {
        // 可以根据实际业务规则调整约束条件
        return new SchedulingConstraints();
    }

    /**
     * 从course_schedule_details表加载所有待排课的课程班信息
     */
    public Map<String, CourseClassInfo> loadCourseClasses(int semesterId, int academicYear) {
        Map<String, CourseClassInfo> courseClasses = new HashMap<>();

        try {
            logger.info("加载课程班 - 学年: {}, 学期ID: {}", academicYear, semesterId);
            // 使用Mapper接口获取未排课的课程班列表
            List<CourseScheduleDetails> courseDetailsList =
                    courseScheduleDetailsMapper.findCoursesBySemesterAndYearAndStatus(
                            semesterId, academicYear, 0); // 0表示未排课
            logger.info("查询到待排课课程班数量: {}", courseDetailsList.size());

            // 处理查询结果
            for (CourseScheduleDetails courseDetails : courseDetailsList) {
                CourseClassInfo courseClass = createCourseClassFromEntity(courseDetails);
                if (courseClass != null) {
                    courseClasses.put(courseClass.getId(), courseClass);
                }
            }

            logger.info("成功加载 {} 个待排课课程班", courseClasses.size());

        } catch (Exception e) {
            logger.error("加载课程班信息时发生错误", e);
        }

        return courseClasses;
    }

    /**
     * 从CourseScheduleDetails实体创建课程班信息对象
     */
    private CourseClassInfo createCourseClassFromEntity(CourseScheduleDetails courseDetails) {
        try {
            // 提取必需字段
            String id = courseDetails.getId();
            String courseId = courseDetails.getCourseId();
            String teacherId = courseDetails.getTeacherId();
            String administrativeClassId = courseDetails.getAdministrativeClassId(); // 学生群体ID
            String className = courseDetails.getClassName();
            String department = courseDetails.getDepartment();

            // 提取数值字段并设置默认值
            int lessonCount = getIntValue(courseDetails.getLessonCount(), DEFAULT_LESSON_COUNT);
            int weekType = getIntValue(courseDetails.getWeekType(), DEFAULT_WEEK_TYPE);
            int startWeek = getIntValue(courseDetails.getStartWeek(), 1);
            int endWeek = getIntValue(courseDetails.getEndWeek(), 16);
            int maxCapacity = getIntValue(courseDetails.getMaxCapacity(), 60);
            int currentEnrollment = getIntValue(courseDetails.getCurrentEnrollment(), 0);
            int schedulePriority = getIntValue(courseDetails.getSchedulePriority(), 2);
            int semester = getIntValue(courseDetails.getSemester(), 0);
            int academicYear = getIntValue(courseDetails.getAcademicYear(), 0);
            int campus = getIntValue(courseDetails.getCampus(), 1); // 校区默认1
            int roomType = getIntValue(courseDetails.getRoomType(), 1); // 教室类型默认1

            // 确定学生人数：优先使用当前注册人数，如果没有则使用最大容量
            int studentCount = currentEnrollment > 0 ? currentEnrollment : maxCapacity;

            // 创建课程班信息对象
            CourseClassInfo courseClass = new CourseClassInfo(
                    id, courseId, teacherId,
                    administrativeClassId, lessonCount, weekType,
                    startWeek, endWeek, studentCount, schedulePriority,
                    semester, academicYear, className, department,
                    campus, lessonCount, weekType // 校区+每周次数+每次课时
            );

            // 设置教室类型
            courseClass.setRoomType(roomType);

            return courseClass;

        } catch (Exception e) {
            logger.error("创建课程班对象失败 - 课程ID: {}", courseDetails.getId(), e);
            return null;
        }
    }

    /**
     * 从课程班信息中提取教师信息
     */
    private Map<String, TeacherInfo> extractTeachersFromCourseClasses(
            Map<String, CourseClassInfo> courseClasses) {

        Map<String, TeacherInfo> teachers = new HashMap<>();

        // 遍历所有课程班，提取教师信息
        for (CourseClassInfo courseClass : courseClasses.values()) {
            String teacherId = courseClass.getPreferredTeacherId();

            if (teacherId != null && !teacherId.trim().isEmpty()) {
                // 如果教师不存在，创建新的教师信息
                if (!teachers.containsKey(teacherId)) {
                    teachers.put(teacherId, new TeacherInfo(teacherId));
                    logger.debug("新增教师 - ID: {}", teacherId);
                }

                // 添加教师能教授的课程
                teachers.get(teacherId).addTeachableCourse(courseClass.getCourseId());

                // 添加教师分配的课程班
                teachers.get(teacherId).addAssignedCourseClass(courseClass.getId());
            }
        }

        logger.info("从课程班提取教师信息完成: {} 位教师", teachers.size());
        return teachers;
    }

    /**
     * 从resource_availability表加载资源占用情况
     */
    private void loadResourceOccupancy(SchedulingData data, int semesterId, String academicYear) {
        try {
            // 1. 加载教师占用情况
            for (TeacherInfo teacher : data.getTeachers().values()) {
                String teacherId = teacher.getId();
                List<ResourceAvailability> teacherOccupancies =
                        resourceAvailabilityMapper.selectByResourceIdAndAcademicYear(teacherId, academicYear);

                Set<TimeSlot> teacherSlots = teacherOccupancies.stream()
                        .map(TimeSlot::fromResourceAvailability)
                        .collect(Collectors.toSet());

                data.getTeacherOccupancy().get(teacherId).addAll(teacherSlots);
                logger.debug("加载教师占用 - ID: {}, 占用时间段: {} 个", teacherId, teacherSlots.size());
            }

            // 2. 加载教室占用情况（保持教室占用记录但没有教室信息）
            // 从数据库获取所有教室ID
            classroom query = new classroom();
            List<classroom> classroomList = classroomService.selectclassroomList(query);

            if (classroomList != null && !classroomList.isEmpty()) {
                for (classroom classroom : classroomList) {
                    String classroomId = classroom.getId();

                    if (classroomId == null || classroomId.trim().isEmpty()) {
                        logger.warn("发现无效教室ID，已跳过");
                        continue;
                    }

                    try {
                        List<ResourceAvailability> classroomOccupancies =
                                resourceAvailabilityMapper.selectByResourceIdAndAcademicYear(classroomId, academicYear);

                        Set<TimeSlot> classroomSlots = classroomOccupancies.stream()
                                .map(TimeSlot::fromResourceAvailability)
                                .collect(Collectors.toSet());

                        data.getClassroomOccupancy().putIfAbsent(classroomId, new HashSet<>());
                        data.getClassroomOccupancy().get(classroomId).addAll(classroomSlots);
                        logger.debug("加载教室占用 - ID: {}, 占用时间段: {} 个", classroomId, classroomSlots.size());

                    } catch (Exception e) {
                        logger.error("加载教室 {} 占用情况失败", classroomId, e);
                    }
                }
            }

            // 3. 加载学生占用情况（修改后：不依赖群体ID，直接按学生ID存储）
            for (CourseClassInfo courseClass : data.getCourseClasses().values()) {
                String courseClassId = courseClass.getId();

                try {
                    // 3.1 直接通过课程班ID获取学生列表（无需群体ID）
                    List<String> studentIds = courseStudentInfoMapper.selectStudentIdsByCourseScheduleDetailsId(courseClassId);

                    // 新增：保存课程班与学生ID的映射关系
                    if (studentIds == null || studentIds.isEmpty()) {
                        logger.warn("课程班 {} 未查询到学生ID列表", courseClassId);
                        // 即使为空也存入空列表，避免后续NPE
                        studentIdsByCourseClassId.put(courseClassId, new ArrayList<>());
                        continue;
                    }

                    // 存储有效的学生ID列表
                    studentIdsByCourseClassId.put(courseClassId, new ArrayList<>(studentIds));
                    logger.debug("课程班 {} 关联学生数量: {}", courseClassId, studentIds.size());

                    // 3.2 逐个学生查询并存储占用资源（类似教师/教室的处理方式）
                    for (String studentId : studentIds) {
                        // 查询学生的占用资源
                        List<ResourceAvailability> studentOccupancies =
                                resourceAvailabilityMapper.selectByResourceIdAndAcademicYear(studentId, academicYear);

                        // 转换为TimeSlot
                        Set<TimeSlot> studentSlots = studentOccupancies.stream()
                                .map(TimeSlot::fromResourceAvailability)
                                .filter(TimeSlot::isValid)
                                .collect(Collectors.toSet());

                        // 直接存储到学生占用Map中（key为学生ID）
                        data.getStudentOccupancy().computeIfAbsent(studentId, k -> new HashSet<>()).addAll(studentSlots);
                        logger.debug("加载学生占用 - 学生ID: {}, 课程班: {}, 占用时间段: {} 个",
                                studentId, courseClassId, studentSlots.size());
                    }
                } catch (Exception e) {
                    logger.error("加载课程班 {} 学生占用情况失败", courseClassId, e);
                    // 发生异常时仍存入空列表，避免后续NPE
                    studentIdsByCourseClassId.putIfAbsent(courseClassId, new ArrayList<>());
                }
            }

        } catch (Exception e) {
            logger.error("加载资源占用情况时发生错误", e);
        }
    }


    /**
     * 将TimetableVO转换为TimeSlot集合
     */
    private Set<TimeSlot> convertTimetableToTimeSlots(TimetableVO timetableVO) {
        Set<TimeSlot> timeSlots = new HashSet<>();

        if (timetableVO == null) {
            logger.warn("TimetableVO为空，无法转换为TimeSlot");
            return timeSlots;
        }

        // 遍历所有星期
        for (TimetableVO.Weekday weekday : timetableVO.getWeekdays()) {
            if (weekday == null) {
                continue;
            }

            // 获取星期几（1-7）
            Long dayOfWeekLong = weekday.getDayOfWeek();
            if (dayOfWeekLong == null || dayOfWeekLong < 1 || dayOfWeekLong > 7) {
                logger.warn("无效的星期几: {}", dayOfWeekLong);
                continue;
            }
            int dayOfWeek = dayOfWeekLong.intValue();

            // 遍历当天的所有时间段
            for (TimetableVO.TimeSlot timeSlot : weekday.getTimeSlots()) {
                if (timeSlot == null) {
                    continue;
                }

                // 遍历该时间段内的所有课程/占用信息
                for (TimetableVO.CourseOccupancy course : timeSlot.getCourses()) {
                    if (course == null) {
                        continue;
                    }

                    // 获取节次信息
                    Integer startLesson = course.getStartLesson();
                    Integer endLesson = course.getEndLesson();
                    if (startLesson == null || endLesson == null || startLesson > endLesson) {
                        logger.warn("无效节次信息 - 开始: {}, 结束: {}", startLesson, endLesson);
                        continue;
                    }

                    // 根据节次获取对应的时间
                    LocalTime startTime = TimeSlot.SESSION_START_TIMES.get(startLesson);
                    LocalTime endTime = TimeSlot.SESSION_END_TIMES.get(endLesson);

                    // 处理跨节次的情况（如第2-3节）
                    if (startLesson != endLesson && endTime == null) {
                        // 如果结束节次不存在，使用最后一个有效节次的结束时间
                        for (int i = endLesson; i >= startLesson; i--) {
                            endTime = TimeSlot.SESSION_END_TIMES.get(i);
                            if (endTime != null) {
                                break;
                            }
                        }
                    }

                    if (startTime == null || endTime == null) {
                        logger.warn("无法解析节次时间 - 节次: {}-{}", startLesson, endLesson);
                        continue;
                    }

                    // 从额外信息中获取周数（如果存在）
                    Integer weekNumber = 1; // 默认值
                    if (course.getExtraInfo() != null && course.getExtraInfo().containsKey("weekNumber")) {
                        Object weekObj = course.getExtraInfo().get("weekNumber");
                        if (weekObj instanceof Integer) {
                            weekNumber = (Integer) weekObj;
                        } else if (weekObj instanceof String) {
                            try {
                                weekNumber = Integer.parseInt((String) weekObj);
                            } catch (NumberFormatException e) {
                                logger.error("周数转换失败: {}", weekObj, e);
                            }
                        }
                    }

                    // 创建并添加TimeSlot
                    TimeSlot slot = TimeSlot.fromDatabase(dayOfWeek,
                            startTime.toString(),
                            endTime.toString());
                    if (slot.isValid()) {
                        timeSlots.add(slot);
                    }
                }
            }
        }

        return timeSlots;
    }

    /**
     * 加载所有可用时间段
     */
    private void loadAvailableTimeSlots(SchedulingData data, int semesterId, int academicYear) {
        // 生成可用时间段
        DayOfWeek[] weekdays = {DayOfWeek.MONDAY, DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY,
                DayOfWeek.THURSDAY, DayOfWeek.FRIDAY};

        // 确定最大周数（基于课程班的最大结束周）
        int maxWeek = data.getCourseClasses().values().stream()
                .mapToInt(CourseClassInfo::getEndWeek)
                .max()
                .orElse(16);

        maxWeek = Math.min(maxWeek, MAX_WEEK_NUMBER);
        logger.info("生成可用时间段 - 周数范围: 1-{}, 工作日: 周一至周五", maxWeek);

        // 生成所有时间段（使用节次而非直接时间）
        for (DayOfWeek day : weekdays) {
            // 上午（1-4节）
            for (int i = 1; i <= 4; i++) {
                data.getAllAvailableTimeSlots().add(new TimeSlot(day, i, i));
            }

            // 下午（5-8节）
            for (int i = 5; i <= 8; i++) {
                data.getAllAvailableTimeSlots().add(new TimeSlot(day, i, i));
            }

            // 晚上（9-10节，周一至周四）
            if (day.getValue() <= DayOfWeek.THURSDAY.getValue()) {
                for (int i = 9; i <= 10; i++) {
                    data.getAllAvailableTimeSlots().add(new TimeSlot(day, i, i));
                }
            }
        }

        logger.info("生成可用时间段完成: {} 个", data.getAllAvailableTimeSlots().size());
    }

    /**
     * 打印处理摘要
     */
    private void printProcessingSummary(SchedulingData data) {
        logger.info("===== 排课数据处理摘要 =====");
        logger.info("课程班数量: {}", data.getCourseClasses().size());
        logger.info("教师数量: {}", data.getTeachers().size());
        logger.info("可用时间段数量: {}", data.getAllAvailableTimeSlots().size());
        logger.info("学生-课程班映射数量: {}", studentIdsByCourseClassId.size());

        // 计算总课时需求和总学生人数
        int totalSessions = data.getCourseClasses().values().stream()
                .mapToInt(CourseClassInfo::getTotalSessionsNeeded)
                .sum();

        int totalStudents = data.getCourseClasses().values().stream()
                .mapToInt(CourseClassInfo::getStudentCount)
                .sum();

        logger.info("总课时需求: {}", totalSessions);
        logger.info("总学生人数: {}", totalStudents);
        logger.info("严重错误数: {}", data.getValidationErrors().size());
        logger.info("冲突警告数: {}", data.getConflictWarnings().size());
    }

    /**
     * 安全获取整数值（处理null和类型转换）
     */
    private int getIntValue(Integer value, int defaultValue) {
        return value != null ? value : defaultValue;
    }

    /**
     * 处理Long类型的重载方法
     */
    private int getIntValue(Long value, int defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        // 检查是否超出Integer范围
        if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) {
            logger.error("数值超出Integer范围: {}, 使用默认值: {}", value, defaultValue);
            return defaultValue;
        }
        return value.intValue();
    }

    // 其他辅助方法
    public List<CourseClassInfo> getCourseClassesByPriority(Map<String, CourseClassInfo> courseClasses, int priority) {
        List<CourseClassInfo> result = new ArrayList<>();
        for (CourseClassInfo courseClass : courseClasses.values()) {
            if (courseClass.getPriority() == priority) {
                result.add(courseClass);
            }
        }
        return result;
    }

    public List<CourseClassInfo> sortByPriority(Map<String, CourseClassInfo> courseClasses) {
        List<CourseClassInfo> result = new ArrayList<>(courseClasses.values());
        result.sort(Comparator.comparingInt(CourseClassInfo::getPriority));
        return result;
    }

    public List<CourseClassInfo> sortByStudentCount(Map<String, CourseClassInfo> courseClasses) {
        List<CourseClassInfo> result = new ArrayList<>(courseClasses.values());
        result.sort((c1, c2) -> Integer.compare(c2.getStudentCount(), c1.getStudentCount()));
        return result;
    }

    public List<CourseClassInfo> sortBySessionCount(Map<String, CourseClassInfo> courseClasses) {
        List<CourseClassInfo> result = new ArrayList<>(courseClasses.values());
        result.sort((c1, c2) -> Integer.compare(c2.getTotalSessionsNeeded(), c1.getTotalSessionsNeeded()));
        return result;
    }

    public int getTotalSessionsNeeded(Map<String, CourseClassInfo> courseClasses) {
        return courseClasses.values().stream()
                .mapToInt(CourseClassInfo::getTotalSessionsNeeded)
                .sum();
    }

    public int getTotalStudents(Map<String, CourseClassInfo> courseClasses) {
        return courseClasses.values().stream()
                .mapToInt(CourseClassInfo::getStudentCount)
                .sum();
    }

    public List<String> getCourseClassesByTeacher(
            Map<String, TeacherInfo> teachers, String teacherId) {

        if (teachers.containsKey(teacherId)) {
            return new ArrayList<>(teachers.get(teacherId).getAssignedCourseClasses());
        }

        return Collections.emptyList();
    }

    public List<TeacherInfo> sortByWorkload(Map<String, TeacherInfo> teachers) {
        List<TeacherInfo> result = new ArrayList<>(teachers.values());
        result.sort((t1, t2) -> Integer.compare(t2.getWorkload(), t1.getWorkload()));
        return result;
    }

    public Map<Integer, Integer> getWorkloadDistribution(Map<String, TeacherInfo> teachers) {
        Map<Integer, Integer> distribution = new HashMap<>();

        for (TeacherInfo teacher : teachers.values()) {
            int workload = teacher.getWorkload();
            distribution.put(workload, distribution.getOrDefault(workload, 0) + 1);
        }

        return distribution;
    }

    /**
     * 打印完整的预处理数据，增强了详细程度
     */
    public void printFullSchedulingData(SchedulingData data) {
        logger.info("\n===== 完整预处理数据 =====");

        // 1. 打印课程班详细信息
        logger.info("\n----- 课程班详细信息 ({}) -----", data.getCourseClasses().size());
        for (CourseClassInfo course : data.getCourseClasses().values()) {
            logger.info("课程班ID: {}", course.getId());
            logger.info("  班级名称: {}", course.getClassName());
            logger.info("  课程ID: {}", course.getCourseId());
            logger.info("  教师ID: {}", course.getPreferredTeacherId());
            logger.info("  学生群体ID: {}", course.getStudentGroupId());
            logger.info("  每周上课次数: {}", course.getLessonCount());
            logger.info("  每次课课时数: {}", course.getWeekType());
            logger.info("  周范围: {}-{}周", course.getStartWeek(), course.getEndWeek());
            logger.info("  学生数量: {}", course.getStudentCount());
            logger.info("  优先级: {}", course.getPriority());
            logger.info("  校区: {}", course.getCampus());
            logger.info("  院系: {}", course.getDepartment());
            logger.info("  总课时需求: {}", course.getTotalSessionsNeeded());
            logger.info("  关联学生数量: {}",
                    studentIdsByCourseClassId.containsKey(course.getId()) ?
                            studentIdsByCourseClassId.get(course.getId()).size() : 0);
        }

        // 2. 打印教师详细信息
        logger.info("\n----- 教师详细信息 ({}) -----", data.getTeachers().size());
        for (TeacherInfo teacher : data.getTeachers().values()) {
            logger.info("教师ID: {}", teacher.getId());
            logger.info("  可教课程数: {}", teacher.getTeachableCourses().size());
            logger.info("  可教课程ID: {}", teacher.getTeachableCourses());
            logger.info("  分配课程数: {}", teacher.getAssignedCourseClasses().size());
            logger.info("  分配课程班ID: {}", teacher.getAssignedCourseClasses());
        }

        // 3. 打印教师占用时间详细信息
        logger.info("\n----- 教师占用时间详细信息 -----");
        for (Map.Entry<String, Set<TimeSlot>> entry : data.getTeacherOccupancy().entrySet()) {
            logger.info("教师ID: {}, 占用时间槽数量: {}", entry.getKey(), entry.getValue().size());
            for (TimeSlot slot : entry.getValue()) {
                logger.info("  - {}", slot);
            }
        }

        // 4. 打印教室占用时间详细信息
        logger.info("\n----- 教室占用时间详细信息 -----");
        for (Map.Entry<String, Set<TimeSlot>> entry : data.getClassroomOccupancy().entrySet()) {
            logger.info("教室ID: {}, 占用时间槽数量: {}", entry.getKey(), entry.getValue().size());
            for (TimeSlot slot : entry.getValue()) {
                logger.info("  - {}", slot);
            }
        }

        // 5. 打印学生占用时间详细信息
        logger.info("\n----- 学生占用时间详细信息 -----");
        for (Map.Entry<String, Set<TimeSlot>> entry : data.getStudentOccupancy().entrySet()) {
            logger.info("学生ID: {}, 占用时间槽数量: {}", entry.getKey(), entry.getValue().size());
            if (logger.isDebugEnabled()) {  // 学生数量可能很多，使用debug级别控制详细输出
                for (TimeSlot slot : entry.getValue()) {
                    logger.debug("  - {}", slot);
                }
            }
        }

        // 6. 打印可用时间段详细信息
        logger.info("\n----- 可用时间详细信息 -----");
        logger.info("可用时间段总数: {}", data.getAllAvailableTimeSlots().size());
        if (logger.isDebugEnabled()) {
            List<TimeSlot> sortedSlots = new ArrayList<>(data.getAllAvailableTimeSlots());
            sortedSlots.sort((s1, s2) -> {
                if (!s1.getDayOfWeek().equals(s2.getDayOfWeek())) {
                    return s1.getDayOfWeek().compareTo(s2.getDayOfWeek());
                }
                return Integer.compare(s1.getStartSession(), s2.getStartSession());
            });

            DayOfWeek currentDay = null;
            for (TimeSlot slot : sortedSlots) {
                if (!slot.getDayOfWeek().equals(currentDay)) {
                    currentDay = slot.getDayOfWeek();
                    logger.debug("\n{}:", slot.getChineseDay(currentDay));
                }
                logger.debug("  {}", slot);
            }
        }

        // 7. 打印校验结果详细信息
        logger.info("\n----- 数据校验结果详细信息 -----");
        logger.info("严重错误数: {}", data.getValidationErrors().size());
        for (int i = 0; i < data.getValidationErrors().size(); i++) {
            logger.error("  错误 {}: {}", i + 1, data.getValidationErrors().get(i));
        }

        logger.info("冲突警告数: {}", data.getConflictWarnings().size());
        for (int i = 0; i < data.getConflictWarnings().size(); i++) {
            logger.warn("  警告 {}: {}", i + 1, data.getConflictWarnings().get(i));
        }
    }
}
